﻿
#region Usings
using System;

using System.Text;
using System.IO;
using System.Xml.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Xml;
using System.Web.Script.Serialization;
using System.Runtime.Serialization.Json;
#endregion

namespace YouWei.Utility
{
    /// <summary>
    /// 序列化操作类
    /// </summary>
    public static class SerializationHelper
    {
        private static object obj = new object(); 
        #region Extension Methods

        #region ToBinary

        /// <summary>
        /// 将一个对象序列化字节数组
        /// </summary>
        /// <param name="Object">要序列化的对象</param>
        /// <param name="FileToSaveTo">要将序列化的结果存放的路径地址（可选）,但务必为正确的全路径形式，否则会报错</param>
        /// <returns>序列化后的字节数组</returns>
        public static byte[] ToBinary(this object Object, string FileToSaveTo = "")
        {
            if (Object == null)
                throw new ArgumentNullException("Object");
            byte[] Output = new byte[0];
            using (MemoryStream Stream = new MemoryStream())
            {
                BinaryFormatter Formatter = new BinaryFormatter();
                Formatter.Serialize(Stream, Object);
                Output = Stream.ToArray();
            }
            if (!string.IsNullOrEmpty(FileToSaveTo))
                new FileInfo(FileToSaveTo).Save(Output);
            return Output;
        }

        #endregion

        #region ToJSON

        /// <summary>
        /// 将一个对象序列化为Json字符串，返回此Json字符串,如果不指定编码格式将使用JavaScriptSerializer的方式进行序列化，否则将使用DataContractJsonSerializer的方式来进行序列化；当使用DataContractJsonSerializer序列化时，为确保生成干净的Json字符串，请对要序列的类，对象属性分别使用DataContract和DataMember等相关特性来标记
        /// </summary>
        /// <param name="Object">要序列化的对象</param>
        /// <param name="FileToSaveTo">要将序列化的结果存放的路径地址（可选）,但务必为正确的全路径形式，否则会报错</param>
        /// <param name="EncodingUsing">编码格式</param>
        /// <returns>转化后的Json字符串，如果</returns>
        public static string ToJSON(this object Object, string FileToSaveTo = "", Encoding EncodingUsing = null)
        {
            if (Object == null)
                throw new ArgumentNullException("Object");
            string JSON = "";
            if (EncodingUsing == null)
            {
                //EncodingUsing = new UTF8Encoding();

                JavaScriptSerializer serializer = new JavaScriptSerializer();
                serializer.MaxJsonLength = 20000000;//20M
                JSON = serializer.Serialize(Object);
            }
            else
            {
                using (MemoryStream Stream = new MemoryStream())
                {
                    DataContractJsonSerializer Serializer = new DataContractJsonSerializer(Object.GetType());
                    Serializer.WriteObject(Stream, Object);
                    Stream.Flush();
                    JSON = EncodingUsing.GetString(Stream.GetBuffer(), 0, (int)Stream.Position);
                }
            }
            if (!string.IsNullOrEmpty(FileToSaveTo))
            {
                new FileInfo(FileToSaveTo).Save(JSON);
            }
            return JSON;
        }

        #endregion

        #region ToXML

        /// <summary>
        /// 将对象序列化为XML格式，并返回序列化后的Xml字符串
        /// </summary>
        /// <param name="Object">要序列化的对象</param>
        /// <param name="FileToSaveTo">要将序列化的结果存放的路径地址（可选）,但务必为正确的全路径形式，否则会报错</param>
        /// <param name="EncodingUsing">使用的编码格式,默认为UTF-8</param>
        /// <returns>序列化后的XML字符串</returns>
        public static string ToXML(this object Object, string FileToSaveTo = "", Encoding EncodingUsing = null)
        {
            if (Object == null)
            {
                throw new ArgumentNullException("Object");
            }
               
            if (EncodingUsing == null)
            {
                EncodingUsing = new UTF8Encoding();
            }
                
            string XML;
            using (MemoryStream Stream = new MemoryStream())
            {
                XmlSerializer Serializer = new XmlSerializer(Object.GetType());
                Serializer.Serialize(Stream, Object);
                Stream.Flush();
                XML = EncodingUsing.GetString(Stream.GetBuffer(), 0, (int)Stream.Position);
            }
            if (!string.IsNullOrEmpty(FileToSaveTo))
            {
                new FileInfo(FileToSaveTo).Save(XML);
            }
            return XML;
        }

        /// <summary>
        /// 序列化为使用CDATA包裹节点内容的XML
        /// </summary>
        /// <param name="Object">将要转化为CDataXml的对象</param>
        /// <param name="FileToSaveTo">要将序列化的结果存放的路径地址（可选）,但务必为正确的全路径形式，否则会报错</param>
        /// <param name="EncodingUsing">使用的编码格式,默认为UTF-8</param>
        /// <returns></returns>
        public static string ToCDataXml(this object Object, string FileToSaveTo = "", Encoding EncodingUsing = null)
        {
            if (Object == null)
            {
                throw new ArgumentNullException("Object");
            }

            if (EncodingUsing == null)
            {
                EncodingUsing = new UTF8Encoding();
            }

            string XML = string.Empty; ;
            if (!string.IsNullOrEmpty(FileToSaveTo))
            {
                lock (obj)
                {
                    XmlSerializer serializer = new XmlSerializer(Object.GetType());

                    // Create an XmlSerializerNamespaces object.
                    XmlSerializerNamespaces ns = new XmlSerializerNamespaces();

                    // Add two namespaces with prefixes.
                    ns.Add("xsd", "http://www.w3.org/2001/XMLSchema");
                    ns.Add("xsi", "http://www.w3.org/2001/XMLSchema-instance");
                    string path = Path.GetDirectoryName(FileToSaveTo);
                    if (!Directory.Exists(path)) Directory.CreateDirectory(path);

                    // Create an XmlTextWriter using a FileStream.
                    Stream fs = new FileStream(FileToSaveTo, FileMode.Create);
                    XmlWriter writer = new MyXMLWriter(fs, new UTF8Encoding());

                    // Serialize using the XmlTextWriter.
                    serializer.Serialize(writer, Object, ns);
                    writer.Close();
                    fs.Close();
                }
            }
            else
            {
                XmlSerializer serializer = new XmlSerializer(Object.GetType());

                // Create an XmlSerializerNamespaces object.
                XmlSerializerNamespaces ns = new XmlSerializerNamespaces();

                // Add two namespaces with prefixes.
                ns.Add("xsd", "http://www.w3.org/2001/XMLSchema");
                ns.Add("xsi", "http://www.w3.org/2001/XMLSchema-instance");
                using (MemoryStream Stream = new MemoryStream())
                {
                    XmlWriter writer = new MyXMLWriter(Stream, EncodingUsing);
                    serializer.Serialize(writer, Object, ns);
                    Stream.Flush();
                    XML = EncodingUsing.GetString(Stream.GetBuffer(), 0, (int)Stream.Position);
                    writer.Close();
                }
            }
            return XML;
        }
        #endregion

        #region ToObject

        /// <summary>
        /// 将序列化的byte数组反序列化为对象
        /// </summary>
        /// <param name="Content">要反列化的byte数组</param>
        /// <typeparam name="T">要转化的对象类型</typeparam>
        /// <returns></returns>
        public static T ToObject<T>(this byte[] Content)
        {
            return (Content == null) ? default(T) : (T)Content.ToObject(typeof(T));
        }

        /// <summary>
        /// 将序列化的byte数组反序列化为对象
        /// </summary>
        /// <param name="Content">要反列化的byte数组</param>
        /// <param name="ObjectType">要转化的对象类型</param>
        /// <returns></returns>
        public static object ToObject(this byte[] Content, Type ObjectType)
        {
            if (Content == null)
                return null;
            using (MemoryStream Stream = new MemoryStream(Content))
            {
                BinaryFormatter Formatter = new BinaryFormatter();
                return Formatter.Deserialize(Stream);
            }
        }

        /// <summary>
        /// 将XmlDocument对象反序列为对象的泛型方法
        /// </summary>
        /// <param name="Content">将要反序列化的XmlDoucment对象</param>
        /// <param name="EncodingUsing">使用的编码格式，默认为UTF-8</param>
        /// <typeparam name="T">序列化的对象T</typeparam>
        /// <returns></returns>
        public static T ToObject<T>(this XmlDocument Content, Encoding EncodingUsing = null)
        {
            return (Content == null) ? default(T) : (T)Content.InnerXml.XMLToObject(typeof(T), EncodingUsing);
        }

        /// <summary>
        /// 将序列化的XmlDocument内容反序列为指定的对象类型
        /// </summary>
        /// <param name="Content">将要反序列化的XmlDoucment对象</param>
        /// <param name="EncodingUsing">使用的编码格式，默认为UTF-8</param>
        /// <param name="ObjectType">要反序列化成的类型</param>
        /// <returns></returns>
        public static object ToObject(this XmlDocument Content, Type ObjectType, Encoding EncodingUsing = null)
        {
            return (Content == null) ? null : Content.InnerXml.XMLToObject(ObjectType, EncodingUsing);
        }
        /// <summary>
        /// 将存储序列化的二进制字节数组文件反序列化为指定类型的对象
        /// </summary>
        /// <typeparam name="T">指定类型的对象</typeparam>
        /// <param name="Content">要反序列化的文件</param>
        /// <returns></returns>
        public static T ToObject<T>(this FileInfo Content)
        {
            return (Content == null || !Content.Exists) ? default(T) : Content.ReadBinary().ToObject<T>();
        }
        #endregion

        #region JSONToObject

        /// <summary>
        /// 将Json字符串反序列化为指定类型T的对象
        /// </summary>
        /// <typeparam name="T">指定的返回类型T</typeparam>
        /// <param name="Content">要转化为的Json字符串</param>
        /// <param name="EncodingUsing">编码格式，默认为UTF-8</param>
        /// <returns>指定类型T的对象</returns>
        public static T JSONToObject<T>(this string Content, Encoding EncodingUsing = null)
        {
            if (EncodingUsing == null)
            {
                JavaScriptSerializer serializer = new JavaScriptSerializer();
                serializer.MaxJsonLength = 20000000;//20M
                return serializer.Deserialize<T>(Content);
            }
            return string.IsNullOrEmpty(Content) ? default(T) : (T)Content.JSONToObject(typeof(T), EncodingUsing);
        }
       
        /// <summary>
        /// 将存储Json信息的文件转化为指定类型T的对象
        /// </summary>
        /// <typeparam name="T">指定的返回类型T</typeparam>
        /// <param name="Content">存储Json信息的文件</param>
        /// <param name="EncodingUsing">编码格式，默认为UTF-8</param>
        /// <returns>指定类型T的对象</returns>
        public static T JSONToObject<T>(this FileInfo Content, Encoding EncodingUsing = null)
        {
            if (EncodingUsing == null)
            {
                JavaScriptSerializer serializer = new JavaScriptSerializer();
                serializer.MaxJsonLength = 20000000;//20M
                return serializer.Deserialize<T>(Content.Read());
            }
            return (Content == null || !Content.Exists) ? default(T) : (T)Content.Read().JSONToObject(typeof(T), EncodingUsing);
        }

        /// <summary>
        ///将存储Json信息的文件转化为指定类型ObjectType的对象
        /// </summary>
        /// <param name="ObjectType">指定的返回类型ObjectType</param>
        /// <param name="Content">存储Json信息的文件</param>
        /// <param name="EncodingUsing">编码格式，默认为UTF-8</param>
        /// <returns>>指定类型ObjectType的对象</returns>
        public static object JSONToObject(this FileInfo Content, Type ObjectType, Encoding EncodingUsing = null)
        {
            return (Content == null || !Content.Exists) ? null : Content.Read().JSONToObject(ObjectType, EncodingUsing);
        }


        /// <summary>
        /// 将序列化的Json字符串反序列化为指定类型ObjectType的对象
        /// </summary>
        /// <param name="ObjectType">指定的返回类型ObjectType</param>
        /// <param name="Content">序列化的Json字符串</param>
        /// <param name="EncodingUsing">编码格式，默认为UTF-8</param>
        /// <returns>指定类型ObjectType的对象</returns>
        public static object JSONToObject(this string Content, Type ObjectType, Encoding EncodingUsing = null)
        {
            if (string.IsNullOrEmpty(Content))
                return null;
            if (EncodingUsing == null)
            {
                EncodingUsing = new UTF8Encoding();
            }
            using (MemoryStream Stream = new MemoryStream(EncodingUsing.GetBytes(Content)))
            {
                DataContractJsonSerializer Serializer = new DataContractJsonSerializer(ObjectType);
                return Serializer.ReadObject(Stream);
            }
        }

        #endregion

        #region XMLToObject

        /// <summary>
        /// 将序列化的字符串转化为指定类型T的对象
        /// </summary>
        /// <typeparam name="T">要反序列化成的类型</typeparam>
        /// <param name="Content">要反序列化的字符串</param>
        /// <param name="EncodingUsing">编码格式,默认UTF-8</param>
        /// <returns></returns>
        public static T XMLToObject<T>(this string Content, Encoding EncodingUsing = null)
        {
            return string.IsNullOrEmpty(Content) ? default(T) : (T)Content.XMLToObject(typeof(T), EncodingUsing);
        }

        /// <summary>
        /// 将指定的序列化文件反序列化成指定的类型T对象
        /// </summary>
        /// <typeparam name="T">要反序列化成的类型</typeparam>
        /// <param name="Content">要反序列化的FileInfo</param>
        /// <param name="EncodingUsing">编码格式,默认UTF-8</param>
        /// <returns></returns>
        public static T XMLToObject<T>(this FileInfo Content, Encoding EncodingUsing = null)
        {
            return (Content == null || !Content.Exists) ? default(T) : (T)Content.Read().XMLToObject(typeof(T), EncodingUsing);
        }
        /// <summary>
        /// 将指定的序列化文件反序列化成指定的类型T对象
        /// </summary>
        /// <param name="ObjectType">要反序列化成的类型</param>
        /// <param name="Content">要反序列化的FileInfo</param>
        /// <param name="EncodingUsing">编码格式,默认UTF-8</param>
        /// <returns></returns>
        public static object XMLToObject(this FileInfo Content, Type ObjectType, Encoding EncodingUsing = null)
        {
            return (Content == null || !Content.Exists) ? null : Content.Read().XMLToObject(ObjectType, EncodingUsing);
        }

        /// <summary>
        /// 将序列化的字符串转化为指定类型的对象
        /// </summary>
        /// <param name="ObjectType">要反序列化成的类型</param>
        /// <param name="Content">要反序列化的字符串</param>
        /// <param name="EncodingUsing">编码格式,默认UTF-8</param>
        /// <returns></returns>
        public static object XMLToObject(this string Content, Type ObjectType, Encoding EncodingUsing = null)
        {
            if (string.IsNullOrEmpty(Content))
            {
                return null;
            }
            if (EncodingUsing == null)
            {
                EncodingUsing = new UTF8Encoding();
            }
            using (MemoryStream Stream = new MemoryStream(EncodingUsing.GetBytes(Content)))
            {
                XmlSerializer Serializer = new XmlSerializer(ObjectType);
                return Serializer.Deserialize(Stream);
            }
        }

        #endregion

        #endregion 
    }

    internal class MyXMLWriter : XmlTextWriter
    {
        public MyXMLWriter(Stream w, Encoding encoding)
            : base(w, encoding)
        {

        }

        public override void WriteString(string text)
        {
            if (text == "http://www.w3.org/2001/XMLSchema-instance" || text == "http://www.w3.org/2001/XMLSchema")
                base.WriteString(text);
            else
            {
                this.WriteCData(ReplaceLowOrderASCIICharacters(text).Replace("<![CDATA[", "<![CDATA [").Replace("]]>", "]] >"));
            }
        }

        /// <summary>
        /// 处理低位字符
        /// </summary>
        /// <param name="tmp"></param>
        /// <returns></returns>
        private string ReplaceLowOrderASCIICharacters(string tmp)
        {
            StringBuilder info = new StringBuilder();
            foreach (char cc in tmp)
            {
                int ss = (int)cc;
                if (((ss >= 0) && (ss <= 8)) || ((ss >= 11) && (ss <= 12)) || ((ss >= 14) && (ss <= 32)))
                    info.AppendFormat(" ", ss);//&#x{0:X};
                else info.Append(cc);
            }
            return info.ToString();
        }
    }

}
